package com.se.netty;

import com.se.net.ICMP;
import com.se.net.TunProChannel;
import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.channel.*;
import lombok.extern.slf4j.Slf4j;
import org.drasyl.channel.tun.*;

import java.net.InetAddress;

/**
 * @author hdu_huang
 * @date 2024/2/24 024 23:25.
 */
@Slf4j
public class NettyTun {

    //基于WinDivert/Winpcap/Npcap  jNetPcap/Jpcap/pcap4j win抓包   linux libpcap tun/tap
    //tun模式能取到IP数据包，无法获得ARP数据
    //原始套接字rawsocket+eBPF => libpcap => tcpdump
    //linux tproxy 即 transparent（透明） proxy   [iptables, ip-rule, ip-route]
    public static void main(String[] args) throws Exception{

        EventLoopGroup group = new DefaultEventLoopGroup(1);
        
        try {
            final Bootstrap b = new Bootstrap()
                    .group(group)
                    .channel(TunProChannel.class)
                    .handler(new ChannelInboundHandlerAdapter() {

                        @Override
                        public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {

                            if (msg instanceof Tun6Packet) {
                                ctx.writeAndFlush(msg);
                                return;
                            }

                            //[ICMP, Tun4Packet[id=21492, len=60, src=198.18.1.1, dst=198.18.1.2]]
                            Tun4Packet tun4Packet = (Tun4Packet) msg;
                            String protocol = InetProtocol.protocolByDecimal(tun4Packet.protocol());

                            InetAddress sourceAddress = tun4Packet.sourceAddress();
                            InetAddress destinationAddress = tun4Packet.destinationAddress();

                            if (protocol.equals("ICMP")) {
                                log.info("ICMP: {}", msg);

                                ByteBuf icmp = ((Tun4Packet) msg).content().copy();
                                byte[] data = ((Tun4Packet) msg).data();

                                //checksum 10 src 12 dest 16
                                icmp.setShort(10, 0);
                                icmp.setBytes(12, destinationAddress.getAddress());
                                icmp.setBytes(16, sourceAddress.getAddress());

                                int checksum = ICMP.calculateChecksum(icmp, 0, 20);
                                icmp.setShort(10, checksum);

                                //type(1), code(1), checksum(2) = 0  data[0-3] = 0
                                icmp.setInt(20, 0);

                                int icmpChecksum = ICMP.calculateChecksum(icmp, 20, icmp.readableBytes());
                                icmp.setShort(22, icmpChecksum);

                                msg = new Tun4Packet(icmp);
                            }


                            //可能需要重新计算 IP, ICMP, TCP, UDP的checksum

                            boolean verify = tun4Packet.verifyChecksum();
                            if (!verify) {

                                byte[] data = new byte[tun4Packet.content().readableBytes()];
                                tun4Packet.content().getBytes(0, data);

                                //checksum=0
                                int checksum = ICMP.calculateChecksum(tun4Packet.content(), 0, tun4Packet.internetHeaderLength() * 4);
                                log.error("verifyChecksum: error, {}, {}, {}", protocol, tun4Packet.headerChecksum(), checksum);
                            }


                            log.info("{}, {}", protocol, msg);
                            //log.info("{}", msg);

                            ctx.writeAndFlush(msg);
                            //ctx.fireChannelRead(msg);

                        }

                    });
            final TunChannel ch = (TunChannel) b.bind(new TunAddress("testTun")).sync().channel();

            System.err.println( ch.localAddress());


            var thread = new Thread(() -> {
                try {
                    ch.device().close();
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
                log.error("JVM ShutdownHook Hook!");
            });
            Runtime.getRuntime().addShutdownHook(thread);

            // send/receive messages of type TunPacket...
            ch.closeFuture().sync();

            //System.in.read();
            //ch.device().close();

        }
        finally {

            log.error("Shutting down...");
            group.shutdownGracefully();

        }
    }

    /*
    IP层本身没有超时重传的机制，是无连接不可靠的协议，避免IP分片。   TCP选项:MSS（Maxitum Segment Size）最大分段大小
    IP层数据大于MTU(1500)就会分片, 即使只丢失一片数据也要重新传整个数据报.  UDP容易导致IP分片, TCP不会造成IP分片 往往MSS为1460
    RFC791[ipv4]规定：所有主机必须能够处理576字节的数据报  UDP548字节(576-8-20)  DNS[512]

    IPv4协议头 IHL(4) 头长度默认5(0101)个字长, 默认20字节 ， 和TCP首部一样 默认20字节，最大15*4=60字节
    Total Length包括IP协议头和数据部分的长度, 2^16-1=65535字节, 一般不会超过MTU
    第二个字是控制是否分包[应该避免分片]  同一个IP数据包[分片]Identification是一样的
    第三个字 Time to Live：TTL最大255秒, 每过路由一次TTL减1, 0表示不转发  Protocol(8位)  首位检验和2个字节

    0                   1                   2                   3
    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |Version|  IHL  |Type of Service|          Total Length         |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |         Identification        |Flags|      Fragment Offset    |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |  Time to Live |    Protocol   |         Header Checksum       |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                       Source Address                          |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                    Destination Address                        |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                    Options                    |    Padding    |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+


   TCP面向字节流 没有包的概念[粘包拆包-应用层的概念] 队头阻塞   MSS 三次握手SYN[MTU-40] 最重要的一个选项 MTU - IP和TCP的头
   SYN包TCP Option - Maximum segment size: 1460 bytes  MSS和RMSS取值较小的值  MSS是数据部分的字段,不包括TCP头
   window scale 8, 256倍  代表window扩展的窗口大小, 不等ACK可以继续发送数据的缓冲区大小


    udp头部格式比较简单
    源端地址2字节  目的地址2字节  包长度2字节[包括头]  检验和2字节[头部和数据]  共2个字8字节
    UDP[没有超时重传和ACK机制]一般安全的包大小为576-60-8=508字节  TCP:MSS-1460   UDP:MSS-512

    DNS[53]基于UDP协议,  MSS:512字节, 超过512字节升级为TCP


    TCP Header Format:  RFC9293
    TCP选项在三次握手过程中出现，用于增强连接的功能和性能

    0                   1                   2                   3
    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |          Source Port          |       Destination Port        |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                        Sequence Number                        |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                    Acknowledgment Number                      |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |  Data |       |C|E|U|A|P|R|S|F|                               |
   | Offset| Rsrvd |W|C|R|C|S|S|Y|I|            Window             |
   |       |       |R|E|G|K|H|T|N|N|                               |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |           Checksum            |         Urgent Pointer        |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                           [Options]                           |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                                                               :
   :                             Data                              :
   :

     */
    
    public static void test(){
      
    }
}
